﻿using System;
using System.Collections.Generic;
using System.Reflection;
using TDAPIOLELib;
using NLog;

namespace QCCure.Core
{
    public class QCController
    {
        public string qcHostname { get; set; }
        public string qcDomain { get; set; }
        public string qcProject { get; set; }
        public string qcLoginName { get; set; }
        public string qcPassword { get; set; }
        public string qcUrl
        {
            get
            {
                if (qcUrl == null) { return "http://" + qcHostname + ":8080/qcbin"; }
                else { return qcUrl; }
            }
            set { qcUrl = value; }
        }

        private TDConnection tdConn;
        private string message;

        private QCBugFilter filter = null;
        private IList<QCUser> users = null;
        private IList<QCUserGroup> userGroups = null;
        private IList<string> statusList = null;
        
        private static Logger log = NLog.LogManager.GetCurrentClassLogger();

        /**
         * Constructor creates the Connection object
         * 
         */
        public QCController()
        {
            try
            {
                this.tdConn = createTDConnection();
            }
            catch (QCException e)
            {
                log.Error(e.Message);
                throw;
            }
        }



        /**
		 * create a new TDConnection object or throw QCException on failure
		 * 
		 */
        public TDConnection createTDConnection()
        {
            log.Debug("Creating TD connection.");

            TDConnection tdConn = new TDConnection();

            if (tdConn == null)
            {
                message = "Can't create TDConnection object -- maybe you need to reference TDAPIOLELib?";
                throw new QCException(message);
            }

            return tdConn;
        }

        public bool IsConnected
        {
            get
            {
                return tdConn != null && tdConn.Connected;
            }
        }

        public List<QCProject> connectToQCAndGetProjectList(string qcUrl, string qcLoginName, string qcPassword)
        {
            try
            {
                tdConn.InitConnectionEx(qcUrl);
                tdConn.Login(qcLoginName, qcPassword);


                List list = tdConn.GetAllVisibleProjectDescriptors();

                List<QCProject> returnList = new List<QCProject>();
                foreach (dynamic obj in list)
                {
                    IProjectDescriptor project = obj as IProjectDescriptor;

                    returnList.Add(new QCProject(project));
                }

                return returnList;
            }
            catch (Exception e)
            {
                log.Warn("unable to connect to project in QC");
                log.Warn(e.Message);
                throw;
            }
        }

        /**
         * Establish a connection to Quality Center
         * 
         * @param string qcUrl
         * @param string qcDomain
         * @param string qcProject
         * @param string qcLoginName
         * @param string qcPassword
         * @return TDConnection
         */
        public TDConnection connectToQC(string qcUrl, string qcDomain, string qcProject, string qcLoginName, string qcPassword)
        {
            try
            {
                tdConn.InitConnectionEx(qcUrl);
                tdConn.ConnectProjectEx(qcDomain, qcProject, qcLoginName, qcPassword);

                filter = null;
                users = null;
                userGroups = null;
            }
            catch (Exception e)
            {
                log.Warn("unable to connect to project in QC");
                log.Warn(e.Message);
                throw;
            }

            if (tdConn.Connected)
            {
                message = "Connected";

                if (tdConn.ProjectConnected)
                {
                    message += " to QC project " + tdConn.ProjectName;
                    log.Info(message);
                }
                else
                {
                    message += " to QC but unable to connect to QC Project " + qcProject;
                    log.Warn(message);
                    throw new QCException(message);
                }
            }
            else
            {
                message = "Unable to connect to QC";
                log.Warn(message);
                throw new QCException(message);
            }

            return tdConn;
        }

        public void disconnectFromQC()
        {
            try
            {
                if (tdConn.Connected)
                {
                    tdConn.Disconnect();
                    filter = null;
                    users = null;
                    userGroups = null;
                }
            }
            catch (Exception e)
            {
                message = "Unable to disconnect from QC";
                log.Warn(message + " " + e.Message);
                throw new QCException(message);
            }
        }


        /**
         * find the test sets that will be updated in QC
         * 
         * @param string tsPath
         * @param string tsName
         * @return List of TestSet
        */
        public List retrieveTestSets(string tsPath, string tsName)
        {
            log.Debug("...in retriveTestSet() " + tsPath + " " + tsName);

            TestSetFolder tsFolder = retrieveFolder(tsPath);

            if (tsFolder == null)
            {
                throw new QCException("no TestSetFolder at " + tsPath);
            }

            List tsList = tsFolder.FindTestSets(tsName, false, null);

            if (tsList == null)
            {
                throw new QCException("no TestSets matching " + tsName);
            }

            if (tsList.Count < 1)
            {
                throw new QCException("no TestSets found matching " + tsName);
            }

            return tsList;
        }



        /***
         * get one or more tests that matches a specific test case name
         * 
         * @param string tsFolderName
         * @param string tsTestName
         * @return List of TsTest
         */
        public List retrieveTests(string tsFolderName, string tsTestName)
        {
            TestSetFolder tsFolder = retrieveFolder(tsFolderName);
            log.Debug("tsFolder.Path: " + tsFolder.Path);

            List tsTestList = tsFolder.FindTestInstances(tsTestName, false, null);
            return tsTestList;
        }



        /**
         * get a folder in QC
         * 
         * @param string tsPath
         * @return TestSetFolder
         */
        public TestSetFolder retrieveFolder(string tsPath)
        {
            TestSetFactory tsFactory = (TestSetFactory)tdConn.TestSetFactory;
            TestSetTreeManager tsTreeMgr = (TestSetTreeManager)tdConn.TestSetTreeManager;

            TestSetFolder tsFolder = null;
            try
            {
                tsFolder = (TestSetFolder)tsTreeMgr.get_NodeByPath(tsPath);
            }
            catch (Exception e)
            {
                log.Warn("couldn't get TestSetFolder with path " + tsPath);
                log.Warn(e.Message);
            }

            return tsFolder;
        }

        
        /**
         * get a filter fields
         * 
         * @return List of filer fields
         */
        public QCBugFilter retrieveFilterFields()
        {
            if (tdConn.Connected)
            {
                if (filter == null)
                {
                    BugFactory bugFactory = (BugFactory)tdConn.BugFactory;

                    TDFilter tdFilter = bugFactory.Filter;

                    filter = new QCBugFilter(tdFilter);
                    
                }

                return filter;
            }
            else
            {
                throw new Exception("Not connected");
            }
        }

        public IList<string> retrieveStatusList()
        {
            if (statusList == null)
            {
                statusList = new List<string>();

                ICustomization cust = tdConn.Customization;
                ICustomizationLists custLists = cust.Lists;
                try
                {
                    ICustomizationList qcStatusList = custLists.List["Bug Status"];
                    CustomizationListNode node = qcStatusList.RootNode;

                    foreach (CustomizationListNode childNode in node.Children)
                    {
                        statusList.Add(childNode.Name);
                    }
                }
                catch (Exception)
                {
                    // if we cannot find the list, ignore this and let user enter the status manually
                }
            }

            return statusList;
        }


        /**
         * get a folder in QC
         * 
         * @param string tsPath
         * @return TestSetFolder
         */
        public IList<QCUser> retrieveUsers()
        {
            if (tdConn.Connected)
            {
                if (users == null)
                {
                    users = new List<QCUser>();

                    ICustomization qcCustomization = tdConn.Customization;
                    
                    ICustomizationUsers qcUsers = qcCustomization.Users;
                    foreach (ICustomizationUser qcUser in qcUsers.Users)
                    {
                        users.Add(new QCUser(qcUser));
                    }

                    // ICustomizationList custList = qcCustomization.Lists;
                }

                return users;
            }
            else
            {
                throw new Exception("Not connected");
            }
        }

        public QCUser LoggedinUser {
            get {
                if (tdConn.Connected)
                {
                    string loggedInUsername = tdConn.UserName;

                    ICustomization qcCustomization = tdConn.Customization;

                    ICustomizationUsers qcUsers = qcCustomization.Users;
                    foreach (ICustomizationUser qcUser in qcUsers.Users)
                    {
                        if (loggedInUsername.Equals(qcUser.Name))
                        {
                            return new QCUser(qcUser);
                        }
                    }

                    return new QCUser(tdConn.UserName);
                }
                else
                {
                    throw new Exception("Not connected");
                }
            }
        }

       

        /**
         * get a folder in QC
         * 
         * @param string tsPath
         * @return TestSetFolder
         */
        public IList<QCUserGroup> retrieveUserGroups()
        {
            if (tdConn.Connected)
            {
                if (userGroups == null)
                {
                    userGroups = new List<QCUserGroup>();

                    ICustomization qcCustomization = tdConn.Customization;

                    ICustomizationUsersGroups qcUserGroups = qcCustomization.UsersGroups;
                    foreach (ICustomizationUsersGroup qcUserGroup in qcUserGroups.Groups)
                    {
                        userGroups.Add(new QCUserGroup(qcUserGroup));
                    }
                }

                return userGroups;
            }
            else
            {
                throw new Exception("Not connected");
            }
        }
        /**
         * get a folder in QC
         * 
         * @param string tsPath
         * @return TestSetFolder
         */
        public IList<QCBugField> retrieveFields()
        {
            if (tdConn.Connected)
            {
                BugFactory bugFactory = (BugFactory)tdConn.BugFactory;

                IList<QCBugField> fields = new List<QCBugField>();

                foreach (TDField field in bugFactory.Fields)
                {
                    fields.Add(new QCBugField(field));
                }

                return fields;
            }
            else
            {
                throw new Exception("Not connected");
            }
        }

        public void convertStringToFilter(QCBugFilter filter, string filterString)
        {
            try
            {
                BugFactory bugFactory = (BugFactory)tdConn.BugFactory;

                TDFilter tdFilter = bugFactory.Filter;

                tdFilter.Text = filterString;
                foreach (QCBugField field in filter)
                {
                    try
                    {
                        if (tdFilter[field.QCName] != null)
                        {
                            field.Value = tdFilter[field.QCName];
                        }
                    }
                    catch (Exception ex)
                    {
                        log.Warn(ex);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Unable to load the fitler: " + ex.Message, ex);
            }
        }

        public string convertFilterToString(QCBugFilter fieldFilters)
        {
            BugFactory bugFactory = (BugFactory)tdConn.BugFactory;

            TDFilter tdFilter = bugFactory.Filter;

            try
            {
                // reset filter
                tdFilter.Text = "";

                foreach (QCBugField filterField in fieldFilters)
                {
                    tdFilter[filterField.QCName] = filterField.Value;
                }

                // default to non-closed defects if the filter is empty
                if (fieldFilters.Count == 0)
                {
                    tdFilter["BG_STATUS"] = "<> CLOSED";
                }

                return tdFilter.Text;
            }
            catch (Exception e)
            {
                log.Warn("couldn't get TestSetFolder with path " + tdFilter.Text);
                log.Warn(e.Message);
                log.Warn(e.StackTrace);

                throw e;
            }
        }

        /**
         * get a folder in QC
         * 
         * @param string tsPath
         * @return TestSetFolder
         */
        public IList<QCBug> retrieveBugs(IList<QCBugField> fieldFilters)
        {
            BugFactory bugFactory = (BugFactory)tdConn.BugFactory;
            
            TDFilter tdFilter = bugFactory.Filter;
              
            try
            {
                // reset filter
                tdFilter.Text = "";

                foreach (QCBugField filterField in fieldFilters) {
                    tdFilter[filterField.QCName] = filterField.Value;
                }
                
                // default to non-closed defects if the filter is empty
                if (fieldFilters.Count == 0) {
                    // look for all not closed defects assigned to the loggin user
                    tdFilter["BG_RESPONSIBLE"] = tdConn.UserName;
                    tdFilter["BG_STATUS"] = "not CLOSED";
                }

                string filterText = tdFilter.Text;
                List qcBugList = bugFactory.NewList(filterText);

                IList<QCBug> bugList = new List<QCBug>();
                foreach (IBug qcBug in qcBugList)
                {
                    QCBug bug = new QCBug(qcBug);
                    bugList.Add(bug);
                }

                return bugList;
            }
            catch (Exception e)
            {
                log.Warn("couldn't get TestSetFolder with path " + tdFilter.Text);
                log.Warn(e.Message);
                log.Warn(e.StackTrace);

                throw e;
            }
        }
        
        /**
		 * set status for tests in a test set and update in QC
		 * 
		 * @param TestSet testSet
         * @param Dictionary<string, string> testResults - testCaseName, testResult (e.g. "EHR_REF_PAT_0001", "Passed")
		 */
        //public void recordTestSetResults(TestSet testSet, Dictionary<string, string> testResults)
        //{
        //    TestSetFolder tsFolder = (TestSetFolder)testSet.TestSetFolder;
        //    log.Debug("tsFolder.Path: " + tsFolder.Path);

        //    string testSetInfo = "testSet.ID: " + testSet.ID.ToString() + DELIM +
        //                         "testSet.Name: " + testSet.Name + DELIM +
        //                         "testSet.Status: " + testSet.Status + DELIM +
        //                         "";

        //    log.Debug("testSetInfo: " + testSetInfo);

        //    TSTestFactory tsTestFactory = (TSTestFactory)testSet.TSTestFactory;
        //    List tsTestList = tsTestFactory.NewList("");

        //    foreach (TSTest tsTest in tsTestList)
        //    {
        //        testCount++;

        //        string testInfo = DELIM + DELIM + DELIM +
        //                        "TestId: " + tsTest.TestId + DELIM +
        //                        "TestName: " + tsTest.TestName + DELIM +
        //                        "";

        //        Run lastRun = (Run)tsTest.LastRun;
        //        if (lastRun != null)
        //        {
        //            testInfo += lastRun.Name + DELIM + lastRun.Status;
        //        }

        //        log.Debug("TestInfo: " + testInfo);

        //        // look for a test in the results from this test set
        //        if (testResults.ContainsKey(tsTest.TestName))
        //        {
        //            string status = testResults[tsTest.TestName];
        //            recordTestResult(tsTest, status);
        //        }

        //    }
        //}



        /**
         * set status for a single test and update in QC
         * 
         * @param TSTest test - the test to be updated
         * @param string status - "Passed", "Failed", etc.
         */
        //public void recordTestResult(TSTest test, string status)
        //{
        //    string testInfo = DELIM + DELIM + DELIM +
        //        "TestId: " + test.TestId + DELIM +
        //        "TestName: " + test.TestName + DELIM +
        //        "";

        //    Run lastRun = (Run)test.LastRun;
        //    if (lastRun != null)
        //    {
        //        testInfo += lastRun.Name + DELIM + lastRun.Status;
        //    }

        //    log.Debug(testInfo);

        //    RunFactory runFactory = (RunFactory)test.RunFactory;
        //    String date = DateTime.Now.ToString("yyyyMMddhhmmss");
        //    Run run = (Run)runFactory.AddItem("Run" + date);
        //    run.Status = status;
        //    run.Post();
        //}
    }
}
